import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;



public class TextSplitter {

    static  String splitMarker = "SPLIT_HERE_SPLIT_HERE";
    static int overlapLen = 0;
    static List<StepRegex> stepReges = new ArrayList<>();

    static int this_chunkLen = 0;

    static double this_overlapRatio = 0;

    public static List<String> splitText2Chunks(String text, int chunkLen, double overlapRatio) {
        this_chunkLen = chunkLen;
        this_overlapRatio = overlapRatio;
        overlapLen = (int) Math.round(chunkLen * overlapRatio);
        stepReges.add(new StepRegex(Pattern.compile("^(#\\s[^\\\\n]+)\\\\n", Pattern.MULTILINE), chunkLen * 1.4));
        stepReges.add(new StepRegex(Pattern.compile("^(##\\s[^\\\\n]+)\\\\n", Pattern.MULTILINE), chunkLen * 1.4));
        stepReges.add(new StepRegex(Pattern.compile("^(###\\s[^\\\\n]+)\\\\n", Pattern.MULTILINE), chunkLen * 1.4));
        stepReges.add(new StepRegex(Pattern.compile("^(####\\s[^\\\\n]+)\\\\n", Pattern.MULTILINE), chunkLen * 1.4));
        //切两个换行符
        stepReges.add(new StepRegex(Pattern.compile("([\\n]{2})"), chunkLen * 1.4));
        stepReges.add(new StepRegex(Pattern.compile("([\\\\n]{2})"), chunkLen * 1.4));

        stepReges.add(new StepRegex(Pattern.compile("([\\n](?![\\*\\-|>`0-9]))"), chunkLen * 1.8));
        stepReges.add(new StepRegex(Pattern.compile("([\\\\n](?![\\*\\-|>`0-9]))"), chunkLen * 1.8));
        stepReges.add(new StepRegex(Pattern.compile("([\\n])"), chunkLen * 1.4));
        stepReges.add(new StepRegex(Pattern.compile("([\\\\n])"), chunkLen * 1.4));
        stepReges.add(new StepRegex(Pattern.compile("([。]|([a-zA-Z])\\.\\s)"), chunkLen * 1.4));
        stepReges.add(new StepRegex(Pattern.compile("([！]|!\\s)"), chunkLen * 1.4));
        stepReges.add(new StepRegex(Pattern.compile("([？]|\\?\\s)"), chunkLen * 1.6));
        stepReges.add(new StepRegex(Pattern.compile("([；]|;\\s)"), chunkLen * 1.8));
        stepReges.add(new StepRegex(Pattern.compile("([，]|,\\s)"), chunkLen * 2));
        return splitTextRecursively(text, 0, "");

    }



    private static List<String> getSplitTexts(String text, int step) {
        if (step >= stepReges.size()) {
            List<String> result = new ArrayList<>();
            result.add(text);
            return result;
        }

        boolean isMarkdownSplit = step <= 3;
        StepRegex stepRegex = stepReges.get(step);
        Pattern pattern = stepRegex.getPattern();

        Matcher matcher = pattern.matcher(text);
        String match = "";
        if(matcher.find()) {
            match = matcher.group();
            match =  match.replaceAll("\\\\n","\\\\\\\\n");
        }

//      .replace(reg, isMarkdownSplit ? "".concat(splitMarker, "$1") : "$1".concat(splitMarker))
        String replacedText = text.replaceAll(pattern.pattern(), isMarkdownSplit ? splitMarker+match  :  match+splitMarker);
        String[] splitTexts = replacedText.split(splitMarker);
        List<String> filteredTexts = new ArrayList<>();
        for (String part  : splitTexts) {
            filteredTexts.add(part.trim());
        }
        return filteredTexts;
    }


    private static String getOneTextOverlapText(String text, int step) {
        boolean forbidOverlap = step <= 6;
        int maxOverlapLen = (int) (this_chunkLen * 0.4);
        if (forbidOverlap || overlapLen == 0 || step >= stepReges.size()) {
            return "";
        }
        List<String> splitTexts = getSplitTexts(text, step);
        String overlayText = "";
        for (int i = splitTexts.size() - 1; i >= 0; i--) {
            String currentText = splitTexts.get(i);
            String newText = currentText + overlayText;
            int newTextLen = newText.length();
            if (newTextLen > overlapLen) {
                if (newTextLen > maxOverlapLen) {
                    String result = getOneTextOverlapText(newText, step + 1);
                    return result.isEmpty() ? overlayText : result;
                }
                return newText;
            }
            overlayText = newText;
        }
        return overlayText;
    }



    private static List<String> splitTextRecursively(String text, int step, String lastText ) {
        if (text.length() <= this_chunkLen) {
            List<String> result = new ArrayList<>();
            result.add(text);
            return result;
        }



        if (step >= stepReges.size()) {
            if (text.length() < this_chunkLen * 3) {
                List<String> result = new ArrayList<>();
                result.add(text);
                return result;
            }
            List<String> chunks_1 = new ArrayList<>();
            for (int i = 0; i < text.length(); i += this_chunkLen - overlapLen) {
                chunks_1.add(text.substring(i, i + this_chunkLen));
            }
            return chunks_1;
        }

        int maxLen = (int) stepReges.get(step).getMaxLen();
        int minChunkLen = (int) (this_chunkLen * 0.7);
        List<String> splitTexts = getSplitTexts(text, step);
        List<String> chunks = new ArrayList<>();
        for (int i = 0 ; i< splitTexts.size(); i++) {
            String currentText = splitTexts.get(i);
            int currentTextLen = currentText.length();
            int lastTextLen = lastText.length();
            String newText = lastText + currentText;
            int newTextLen = lastTextLen + currentTextLen;
            if (newTextLen > maxLen) {
                if (lastTextLen > minChunkLen) {
                    chunks.add(lastText);
                    lastText = getOneTextOverlapText(lastText, step);// next chunk will start with overlayText
                    i--;
                    continue;
                }
                List<String> innerChunks = splitTextRecursively(newText, step + 1, "");

                String lastChunk = innerChunks.get(innerChunks.size() - 1);
                if (lastChunk.length() < minChunkLen) {
                    chunks.addAll(innerChunks.subList(0, innerChunks.size() - 1));
                    lastText = lastChunk;
                } else {
                    chunks.addAll(innerChunks);
                    lastText = getOneTextOverlapText(lastChunk, step);
                }
                continue;
            }
            lastText = newText;
            if (newTextLen >= this_chunkLen) {
                chunks.add(lastText);
                lastText = getOneTextOverlapText(lastText, step);
            }
        }

        if (!lastText.isEmpty() && !chunks.isEmpty() &&!chunks.get(chunks.size()-1).isEmpty() && !chunks.get(chunks.size() - 1).endsWith(lastText)) {
            if (lastText.length() < this_chunkLen * 0.4) {
                String lastChunk = chunks.get(chunks.size() - 1);
                chunks.set(chunks.size() - 1, lastChunk + lastText);
            } else {
                chunks.add(lastText);
            }
        }
        return chunks;
    }



    private static class StepRegex {
        private Pattern pattern;
        private double maxLen;

        public StepRegex(Pattern pattern, double maxLen) {
            this.pattern = pattern;
            this.maxLen = maxLen;
        }

        public Pattern getPattern() {
            return pattern;
        }
        public double getMaxLen() {
            return maxLen;
        }
    }




}

